//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#include "DataStore.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <vector>
#include <algorithm>
#include <functional>

#include <fstream>
#include <iostream>
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#define LENGTH_LINE 1000000
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
using namespace std;
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
dataStore::dataStore():filename(NULL), 
                       allData(NULL), 
                       numberData(0), 
                       dataSimilarity(NULL), 
                       averageDataDistance(NULL) {}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
dataStore::dataStore(char *filename):allData(NULL), 
                                     numberData(0), 
                                     dataSimilarity(NULL), 
                                     averageDataDistance(NULL) {

  this->filename = new char[strlen(filename) + 1];
  strcpy(this->filename, filename);

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
dataStore::~dataStore() {

  if (this->filename) delete[] this->filename;
  if (this->allData)  delete[] this->allData;

  if (this->dataSimilarity) {
    for (int i = 0; i < this->numberData; i++) 
      if (this->dataSimilarity[i]) 
        delete[] this->dataSimilarity[i];
    delete[] this->dataSimilarity;
  }

  if (this->averageDataDistance) 
    delete[] this->averageDataDistance;

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void dataStore::Filename(char *filename) {

  if (this->filename) delete[] this->filename;
  this->filename = new char[strlen(filename) + 1];
  strcpy(this->filename, filename);

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void dataStore::AllData(const int* allData, int numberData) {

  if (this->allData) delete[] this->allData;
  this->numberData  = numberData;
  this->allData     = new int[this->numberData];
  for (int i = 0; i < this->numberData; i++)
    this->allData[i] = allData[i];

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void dataStore::NumberData(int numberData) {

  this->numberData = numberData;

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void dataStore::DataSimililarity(double **dataSimilarity, int numberData) {

  if (this->dataSimilarity) {
    for (int i = 0; i < this->numberData; i++) 
      if (this->dataSimilarity[i]) 
        delete[] this->dataSimilarity[i];
    delete[] this->dataSimilarity;
  }

  this->numberData = numberData;

  this->dataSimilarity = new double*[this->numberData];
  for (int i = 0; i < this->numberData; i++) 
    this->dataSimilarity[i] = new double[this->numberData];

  for (int i = 0; i < this->numberData; i++)
    for (int j = 0; j < this->numberData; j++)
      this->dataSimilarity[i][j] = dataSimilarity[i][j];

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void dataStore::AverageDataDistance(double *averageDataDistance, int numberData) {

  if (this->averageDataDistance) delete[] this->averageDataDistance;

  this->numberData = numberData;

  this->averageDataDistance = new double[this->numberData];
  for (int i = 0; i < this->numberData; i++)
    this->averageDataDistance[i] = averageDataDistance[i];

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
char* dataStore::Filename(void) {

  return this->filename;

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
int dataStore::AllData(int* allData) {

  if (allData) delete[] allData;
  allData = new int[this->numberData];
  for (int i = 0; i < this->numberData; i++) 
    allData[i] = this->allData[i];
  return this->numberData;

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
int dataStore::NumberData(void) {

  return this->numberData;

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
int dataStore::DataSimililarity(double **dataSimilarity) {

  if (dataSimilarity) {
    for (int i = 0; i < this->numberData; i++) 
      if (dataSimilarity[i]) 
        delete[] dataSimilarity[i];
    delete[] dataSimilarity;
  }

  dataSimilarity = new double*[this->numberData];
  for (int i = 0; i < this->numberData; i++) 
    dataSimilarity[i] = new double[this->numberData];

  for (int i = 0; i < this->numberData; i++)
    for (int j = 0; j < this->numberData; j++)
      dataSimilarity[i][j] = this->dataSimilarity[i][j];

  return this->numberData;

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
int dataStore::AverageDataDistance(double *averageDataDistance) {

  if (averageDataDistance) delete[] averageDataDistance;

  averageDataDistance = new double[this->numberData];
  for (int i = 0; i < this->numberData; i++)
    averageDataDistance[i] = this->averageDataDistance[i];

  return this->numberData;

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
int dataStore::OneData(int position) {

  return (position < this->numberData && position >= 0)?this->allData[position]:0;

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
double dataStore::OneAverageDataDistance(int position) {

  return (position < this->numberData && position >= 0)?this->averageDataDistance[position]:0;

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
int dataStore::ObtainAverageDataDistance(void) {

  if (!this->allData || !this->numberData) return 0;

  if (this->averageDataDistance) 
    delete[] this->averageDataDistance;
  this->averageDataDistance = new double[this->numberData];
  
  for (int i = 0; i < this->numberData; i++) {
    this->averageDataDistance[i] = 0;
    for (int j = 0; j < this->numberData; j++) 
      if (i != j) this->averageDataDistance[i] += this->dataSimilarity[i][j];
    this->averageDataDistance[i] /= (this->numberData - 1);
  }

  return 1;

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void dataStore::RandomFile(int iteration = 1) {

//  if (!filename) return;
//
//  fstream  filestr;
//
//  //Read the data in input file
//  filestr.open (filename, ios::in);
//  if (!filestr.is_open()) return;
//
//  char    *lineInput = new char[1000];
//  int      numberLine = 0;
//
//  while (!filestr.eof()) {
//    filestr.getline(lineInput, 1000);
//    if (strlen(lineInput)) numberLine++;
//  }
//
//  numberSmiles = numberLine;
//  if (allSmiles) delete[] allSmiles;
//  allSmiles = new smileStruct[numberSmiles];
//  numberLine = 0;
//
//  filestr.close();
//  filestr.open (filename, ios::in);
//
//  while (!filestr.eof()) {
//    filestr.getline(lineInput, 1000);
//    if (strlen(lineInput)) allSmiles[numberLine++].InputLine(lineInput);
//  }
//  filestr.close();
//  delete[] lineInput;
//
//  //Random process
//  filestr.open (filename, ios::out);
//
//  vector <int> randomFlag;
//  
//  for (int i = 0; i < numberSmiles; ++i) randomFlag.push_back(i);
//  for (int i = 0; i < iteration; i++)    random_shuffle(randomFlag.begin(), randomFlag.end());
//  
//  for (int i = 0; i < numberSmiles; i++) {
//    char *tmpLineInput = new char[strlen(allSmiles[randomFlag.at(i)].Code()) + strlen(allSmiles[randomFlag.at(i)].Id()) + 3];
//    if (!i) sprintf(tmpLineInput,   "%s %s", allSmiles[randomFlag.at(i)].Code(), allSmiles[randomFlag.at(i)].Id());
//    else    sprintf(tmpLineInput, "\n%s %s", allSmiles[randomFlag.at(i)].Code(), allSmiles[randomFlag.at(i)].Id());
//    filestr.write(tmpLineInput, strlen(tmpLineInput));
//  }
//
//  filestr.close();

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void dataStore::OrderFile(char *initialFileName, char *nameFileClusterMol, int ancestor) {

//  if (!initialFileName && !nameFileClusterMol) return;
//
//  fstream  filestr, clusterStr;
//
//  //Read the data in input file
//  filestr.open(initialFileName, fstream::in);
//  if (!filestr.is_open()) return;
//  char *lineInput = new char[1001];
//  int   numberLine = 0;
//  
//  while (!filestr.eof()) { filestr.getline(lineInput, 1000); if (strlen(lineInput)) numberLine++; }
//
//  numberSmiles = numberLine;
//  if (allSmiles) delete[] allSmiles;
//  allSmiles = new smileStruct[numberSmiles];
//  numberLine = 0;
//
//  filestr.close();
//  filestr.open (initialFileName, fstream::in);
//  while (!filestr.eof()) {
//    filestr.getline(lineInput, 1000);
//    if (strlen(lineInput)) allSmiles[numberLine++].InputLine(lineInput);
//  }
//  filestr.close();
//
//  int   count = 0;
//  long **listMol = new long*[numberSmiles];
//  for (int i = 0; i < numberSmiles; i++) listMol[i] = new long[ancestor + 1];
//
//  clusterStr.open(nameFileClusterMol, fstream::in);
//  if (!clusterStr.is_open()) return;
//
//  while (!clusterStr.eof()) {
//    clusterStr.getline(lineInput, 1000);
//    if (strlen(lineInput)) {
//      for (int i = 0; i < ancestor + 1; i++)
//        listMol[count][i]   = splitPosLineIdCode(lineInput, (i + 1));
//      count++;
//    }
//    if (count > numberSmiles) break;
//  }
//  clusterStr.close();
//
//  delete[] lineInput;
//
//  //Order Process
//  filestr.open (filename, fstream::out);
//
//  orderListAncestor(listMol, numberSmiles, ancestor);
//
//  //Order Cluster File
//  clusterStr.open(nameFileClusterMol, fstream::out);
//  
//  for (int i = 0; i < numberSmiles; i++) {
//    for (int j = 0; j < ancestor + 1; j++) {
//      clusterStr << left;
//      clusterStr.width(12);
//      clusterStr << listMol[i][j] << " ";
//    }
//    clusterStr << endl;
//  }
//
//  clusterStr.close();
//
//  for (int i = 0; i < numberSmiles; i++)
//    for (int j = 0; j < numberSmiles; j++)
//      if (atoi(allSmiles[j].Id()) == listMol[i][0]) {
//        char *tmpLineInput = new char[strlen(allSmiles[j].Code()) + strlen(allSmiles[j].Id()) + 3];
//        sprintf(tmpLineInput, "%s %s\0", allSmiles[j].Code(), allSmiles[j].Id());
//        filestr << tmpLineInput << endl;
//        break;
//      }
//  
//
//  filestr.close();
//
//  for (int i = 0; i < numberSmiles; i++) if (listMol[i]) delete listMol[i];
//  if (listMol) delete[] listMol;

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void dataStore::ReduceFile(char *initialFileName, char *nameFileClusterMol) {
    
//  if (!initialFileName && !nameFileClusterMol) return;
//
//  fstream  filestr, clusterStr;
//
//  //Read the data in input file
//  filestr.open(initialFileName, fstream::in);
//  if (!filestr.is_open()) return;
//  char *lineInput = new char[1001];
//  int   numberLine = 0;
//
//  while (!filestr.eof()) { filestr.getline(lineInput, 1000); if (strlen(lineInput)) numberLine++; }
//
//  numberSmiles = numberLine;
//  if (allSmiles) delete[] allSmiles;
//  allSmiles = new smileStruct[numberSmiles];
//  numberLine = 0;
//
//  filestr.close();
//  filestr.open (initialFileName, fstream::in);
//  while (!filestr.eof()) {
//    filestr.getline(lineInput, 1000);
//    if (strlen(lineInput)) allSmiles[numberLine++].InputLine(lineInput);
//  }
//  filestr.close();
//
//  smileStruct  *tmpClusterMol;
//
//  clusterStr.open(nameFileClusterMol, fstream::in);
//  if (!clusterStr.is_open()) return;
//
//  numberLine = 0;
//  
//  while (!clusterStr.eof()) { clusterStr.getline(lineInput, 1000); if (strlen(lineInput)) numberLine++; }
//
//  int numberClusterSmiles = numberLine;
//  tmpClusterMol = new smileStruct[numberClusterSmiles];
//  numberLine = 0;
//
//  clusterStr.close();
//  clusterStr.open (nameFileClusterMol, fstream::in);
//  while (!clusterStr.eof()) {
//    clusterStr.getline(lineInput, 1000);
//    if (strlen(lineInput)) tmpClusterMol[numberLine++].InputLine(lineInput);
//  }
//  clusterStr.close();
//
//  filestr.open(initialFileName, fstream::out);
//
//  for (int i = 0; i < numberSmiles; i++)
//    for (int j = 0; j < numberLine; j++)
//      if (atoi(allSmiles[i].Id()) == atoi(tmpClusterMol[j].Id())) {
//        char *tmpLineInput = new char[strlen(allSmiles[i].Code()) + strlen(allSmiles[i].Id()) + 3];
//        sprintf(tmpLineInput, "%s %s\0", allSmiles[i].Code(), allSmiles[i].Id());
//        filestr << tmpLineInput << endl;
//        break;
//      }
//
//  filestr.close();
//
//  delete[] tmpClusterMol;
//  delete[] lineInput;

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
int dataStore::splitPosLineIdCode(char *line, int pos) {

  char *pch, *lineTmp = new char[strlen(line) + 1];
  int   count = 1;

  strcpy(lineTmp, line);

  pch = strtok(lineTmp, " ");
  if (pos == 1) return atoi(pch);

  while (pch != NULL) {
    pch = strtok(NULL, " ");
    if (++count == pos) return atoi(pch);
  }

  delete[] lineTmp;

  return -1;

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
int dataStore::orderListAncestor(long **&listMol, int numberSmiles, int ancestor) {

  for (int i = 0; i < ancestor + 1; i++) {

    int *initial = NULL, *final = NULL, numberCicles = 0;

    if (!i) {

      numberCicles  = 1;
      initial       = new int;
      *initial      = 0;
      final         = new int;
      *final        = numberSmiles;
      
    }
    else {
        
      long tmpValue = -1, *listMol_tmp = new long[numberSmiles];
      
      for (int j = 0; j < numberSmiles; j++) 
        listMol_tmp[j] = listMol[j][ancestor - i + 1];
      //MergeSort(listMol_tmp, numberSmiles);

      for (int j = 0; j < numberSmiles; j++) 
        if (listMol_tmp[j] != tmpValue) {
          tmpValue = listMol_tmp[j];
          numberCicles++;
        }

      initial = new int[numberCicles];
      final   = new int[numberCicles];

      numberCicles =  0;
      tmpValue  = -1;
      
      for (int j = 0; j < numberSmiles; j++) {
        if (listMol_tmp[j] != tmpValue) {
          if (numberCicles)
            final[numberCicles - 1] = j;
          initial[numberCicles++] = j;
          tmpValue = listMol_tmp[j];
        }
      }
      
      final[numberCicles - 1] = numberSmiles;
      
      delete[] listMol_tmp;

    }

    if (!initial || !final) continue;
    
    for (int z = 0; z < numberCicles; z++) {
        
      for (int j = initial[z]; j < final[z] - 1; j++)
        for (int k = j; k < final[z]; k++) {
          if (listMol[k][ancestor - i] < listMol[j][ancestor - i]) {
            long *LisMol_tmp = listMol[k];
            listMol[k]       = listMol[j];
            listMol[j]       = LisMol_tmp;
          }
        }
    }

    delete[] initial;
    delete[] final;

  }
    
}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void dataStore::MixArray(long *array, int n) {

  long *array1, *array2;
  int n1, n2, i, j;

  if (n > 1) {
    if (n%2 == 0)   n1 = n2 = (int)n/2;
    else         {  n1 = (int)n/2; n2 = n1 + 1; }

    array1  = new long[n1];
    array2  = new long[n2];

    for (i = 0; i < n1; i++)      array1[i] = array[i];
    for (j = 0; j < n2; i++, j++) array2[j] = array[i];

    MixArray(array1, n1);
    MixArray(array2, n2);
    MixArrayX(array1, n1, array2, n2, array);

    delete[] array1;
    delete[] array2;

  }

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void dataStore::MixArrayX(long *array1, int n1, long *array2, int n2, long *array3) {

  int x1 = 0, x2 = 0, x3 = 0;

  while (x1 < n1 && x2 < n2)
    if (array1[x1] < array2[x2])  array3[x3++] = array1[x1++];
    else                          array3[x3++] = array2[x2++];
  while (x1 < n1) array3[x3++] = array1[x1++];
  while (x2 < n2) array3[x3++] = array2[x2++];

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void dataStore::MergeSort(long *array, int n) {

  MixArray(array, n);

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
